package ch.ethz.fcl.metrobuzz.render.od.flow_view.sankey;

import java.util.ArrayList;
import java.util.List;

import ch.ethz.fcl.metrobuzz.render.od.flow_view.ODComponent;
import ch.ethz.fcl.metrobuzz.spatialindex.object2d.Rectangle;

public class SankeyNodeGroup extends SankeyNode {
	private List<SankeyNode> sankeyNodeList;

	private boolean expand = false;
	private List<SankeyNodeGroup> snGroupList;

	private SankeyNodeGroup parentGroup;

	private SankeyNodeGeometry geo;

	public SankeyNodeGroup(ODSankeyDiagramData data, String id, boolean origin) {
		super(data, id, origin);
		sankeyNodeList = new ArrayList<SankeyNode>();
	}

	public void close() {
		expand = false;
	}

	public void expand() {
		expand = true;
		if (snGroupList == null)
			generateSubGroup();

		calPosition();
	}

	public void generateSubGroup() {
		snGroupList = groupSankeyNodesByVolume(sankeyNodeList);
	}

	public void calPosition() {
		float height = bound.getHeight() - (snGroupList.size() - 1)
				* ODComponent.inner_group_gap;
		float x = bound.getMinX();
		float y = bound.getMaxY();
		for (int i = 0; i < snGroupList.size(); i++) {
			SankeyNodeGroup nodeGroup = snGroupList.get(i);

			if (nodeGroup.hide)
				continue;

			float nodeHeight = nodeGroup.getVolume() * 1.0f / getVolume()
					* height;

			Rectangle nodeBound = new Rectangle(x, y - nodeHeight, x
					+ bound.getWidth(), y);
			nodeGroup.setBound(nodeBound);

			y = y - nodeBound.getHeight()
					- ODComponent.inner_group_gap;

			if (nodeGroup.expand)
				nodeGroup.calPosition();
		}
	}

	public void setGeometry(ODSankeyDiagramData data, Rectangle bound) {
		geo = new SankeyNodeGeometry(data, this, bound);
	}

	public void resetVolume() {
		super.resetVolume();
		sankeyNodeList = new ArrayList<SankeyNode>();
	}

	private List<SankeyNodeGroup> groupSankeyNodesByVolume(
			List<SankeyNode> nodeList) {
		List<SankeyNodeGroup> nodeGroupList = new ArrayList<SankeyNodeGroup>();

		SankeyNode.sortByVolume(nodeList);

		for (int i = 0; i < nodeList.size(); i++) {
			SankeyNode sn = nodeList.get(i);
			Float snvolume = sn.getVolume();

			// if height less than 10, don't split
			float height = bound.getHeight() * snvolume / getVolume();

			if (nodeGroupList.size() >= 4 || height < 10) {
				SankeyNodeGroup sankeyNodeGroup = new SankeyNodeGroup(data,
						"Others", sn.origin);

				// sankeyNodeGroup.setOrder(i + 1);

				for (int j = i; j < nodeList.size(); j++) {
					int[] allVolumes = nodeList.get(j).getAllVolume();
					for (int k = 0; k < allVolumes.length; k++)
						sankeyNodeGroup.addVolume(k, allVolumes[k]);

					sankeyNodeGroup.addSankeyNode(nodeList.get(j));
				}

				if (sankeyNodeGroup.getNodeList().size() == 1)
					sankeyNodeGroup.id = sankeyNodeGroup.getNodeList().get(0).id;

				sankeyNodeGroup.setParent(this);
				nodeGroupList.add(sankeyNodeGroup);
				break;
			} else {
				SankeyNodeGroup sankeyNodeGroup = new SankeyNodeGroup(data,
						sn.getId(), sn.origin);

				sankeyNodeGroup.addSankeyNode(sn);

				int[] allVolumes = sn.getAllVolume();
				for (int k = 0; k < allVolumes.length; k++)
					sankeyNodeGroup.addVolume(k, allVolumes[k]);

				// sankeyNodeGroup.setOrder(i + 1);

				sankeyNodeGroup.setParent(this);
				nodeGroupList.add(sankeyNodeGroup);
			}
		}

		if (nodeGroupList.size() == 1)
			nodeGroupList.get(0).id = this.id;

		SankeyNode.sortByVolume(nodeGroupList);

		return nodeGroupList;
	}

	public void setParent(SankeyNodeGroup parent) {
		this.parentGroup = parent;
	}

	public SankeyNodeGroup getParentGroup() {
		return parentGroup;
	}

	public void addSankeyNode(SankeyNode node) {
		sankeyNodeList.add(node);
	}

	public List<SankeyNode> getNodeList() {
		return sankeyNodeList;
	}

	public List<SankeyNodeGroup> getNodeGroupList() {
		return snGroupList;
	}

	public int size() {
		return sankeyNodeList.size();
	}

	public boolean expanded() {
		return expand;
	}

	public float getVolume() {
		if (!expand)
			return super.getVolume();
		else {
			return getSubVolume();
		}
	}

	private float getSubVolume() {
		if (!expand)
			return getVolume();
		else {
			int volume = 0;
			if (snGroupList != null)
				for (SankeyNodeGroup subGroup : snGroupList)
					volume += subGroup.getSubVolume();
			else
				for (SankeyNode sn : sankeyNodeList)
					volume += sn.getVolume();
			return volume;
		}
	}

	public SankeyNodeGeometry getGeometry() {
		return geo;
	}
}
